package com.ifcreate.infowell.sys.service;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.ifcreate.infowell.common.util.CustomMap;
import com.ifcreate.infowell.common.util.JsonResult;
import com.ifcreate.infowell.entity.InfoWellNode;
import com.ifcreate.infowell.entity.InfoWellRelation;
import com.ifcreate.infowell.entity.PageResult;
import com.ifcreate.infowell.repository.InfoWellRepository;
import com.ifcreate.infowell.vo.NodeSearchVo;
import com.ifcreate.infowell.vo.RelationVo;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author chenzedeng
 * @Email yustart@foxmail.com
 * @Create 2020-10-25 4:51 下午
 */
@Service
@Log4j2
public class SysInfoWellService {

    @Autowired
    private InfoWellRepository repository;

    @Autowired
    private SessionFactory sessionFactory;

    /**
     * 添加或者更新单个节点
     *
     * @param node
     * @return
     */
    public JsonResult addOrUpdate(InfoWellNode node) {
        if (node.getId() != null) {
            //更新
            Optional<InfoWellNode> optional = repository.findById(node.getId());
            if (!optional.isPresent()) {
                return JsonResult.error("节点不存在");
            }
            InfoWellNode originNode = optional.get();
            node.setIsNews(originNode.getIsNews());
            node.setClickCount(originNode.getClickCount());
        } else {
            node.setClickCount(0L);
        }
        InfoWellNode save = repository.save(node);
        return JsonResult.success(save);
    }


    /**
     * 删除节点
     *
     * @param ids
     * @return
     */
    public JsonResult deleteNode(List<Long> ids) {
        Iterable<InfoWellNode> iterable = repository.findAllById(ids);
        repository.deleteAll(iterable);
        return JsonResult.actionSuccess();
    }


    /**
     * 添加关系
     *
     * @param voList
     * @return
     */
    public JsonResult addRelation(List<RelationVo> voList) {
        List<InfoWellRelation> relationList = new ArrayList<>();
        for (RelationVo vo : voList) {
            if (vo.getTarget().equals(vo.getSource())) {
                continue;
            }
            Optional<InfoWellNode> toOptional = repository.findById(vo.getSource());
            if (!toOptional.isPresent()) {
                return JsonResult.error("节点不存在");
            }

            InfoWellNode toNode = toOptional.get();
            Optional<InfoWellNode> fromOptional = repository.findById(vo.getTarget());
            if (!fromOptional.isPresent()) {
                return JsonResult.error("节点不存在");
            }

            //检查是否已经建立了关系
            if (repository.getRelationByStartIdAndEndId(vo.getTarget(), vo.getSource()) != null) {
                continue;
            }

            InfoWellNode formNode = fromOptional.get();

            //检查是否是子节点被大节点链接
            if (formNode.getIsNews() && !toNode.getIsNews()) {
//                return JsonResult.error("子节点不能作为根节点的父节点");
                InfoWellNode tmp = formNode;
                formNode = toNode;
                toNode = tmp;
            }


            InfoWellRelation relationship = new InfoWellRelation();
            relationship.setRelation(vo.getRelation());
            relationship.setFrom(formNode);
            relationship.setTo(toNode);

            relationList.add(relationship);

            formNode.addRelation(relationship);
            repository.save(formNode);
        }
        return JsonResult.success(relationList);
    }


    /**
     * 删除关系 根据ID
     *
     * @param ids
     * @return
     */
    public JsonResult deleteRelationById(List<Long> ids) {
        for (Long id : ids) {
            repository.deleteRelationshipById(id);
        }
        return JsonResult.actionSuccess();
    }

    /**
     * 查询Node节点
     *
     * @param nodeSearchVo
     * @return
     */
    public JsonResult nodePageList(NodeSearchVo nodeSearchVo) {
        StringBuffer sb = new StringBuffer();
        CustomMap params = CustomMap.create("isNews", nodeSearchVo.getIsNews());
        sb.append("match (n) where n.isNews = $isNews ");
        if (nodeSearchVo.getType() != null) {
            params.put("type", nodeSearchVo.getType());
            sb.append("and n.type = $type ");
        }
        if (StringUtils.isNotBlank(nodeSearchVo.getSearch())) {
            sb.append("and ( ")
                    .append(StrUtil.format(" n.name=~ '.*{}.*' ", nodeSearchVo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.title=~ '.*{}.*' ", nodeSearchVo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.infoProvider=~ '.*{}.*' ", nodeSearchVo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.infoContent=~ '.*{}.*' ", nodeSearchVo.getSearch()))
                    .append(") ");
        }

        Session session = sessionFactory.openSession();

        try {

            Iterator<Map<String, Object>> iterator = session.query(sb.toString() + " return count(n) as count", params).iterator();
            if (iterator.hasNext()) {
                Long total = MapUtils.getLong(iterator.next(), "count");
                params.put("size", nodeSearchVo.getSize());
                params.put("skip", (nodeSearchVo.getCurrent() - 1) * nodeSearchVo.getSize());
                iterator = session.query(sb.toString() + " return n order by n.time desc skip $skip limit $size", params).iterator();
                List<InfoWellNode> list = ListUtil.toList(iterator).stream().map(l -> ((InfoWellNode) l.get("n"))).collect(Collectors.toList());


                return JsonResult.success(PageResult.<InfoWellNode>builder()
                        .current(nodeSearchVo.getCurrent())
                        .size(nodeSearchVo.getSize())
                        .pages(PageUtil.totalPage(total.intValue(), nodeSearchVo.getSize()))
                        .total(total)
                        .records(list)
                        .build()
                );
            }
        } finally {
            session.clear();
        }
        return JsonResult.actionFailure();
    }

    /**
     * 查询获取这个节点的 第一级关联的子节点
     *
     * @param id
     * @return
     */
    public JsonResult getNodeRelationByNodeId(Long id) {
        Set<InfoWellRelation> relationList = new HashSet<>();
        relationList.addAll(repository.queryFirstRelationNodeForNext(id));
        relationList.addAll(repository.queryFirstRelationNodeForLast(id));
        return JsonResult.success(relationList);
    }


    /**
     * 查询获取所有的关系
     *
     * @param vo
     * @return
     */
    public JsonResult getRelationList(NodeSearchVo vo) {
        StringBuffer sb = new StringBuffer();
        CustomMap params = CustomMap.create("isNews", vo.getIsNews());
        sb.append("match p = (n)<-[*1..]-() where n.isNews = $isNews ");
        if (vo.getType() != null) {
            params.put("type", vo.getType());
            sb.append("and n.type = $type ");
        }
        if (StringUtils.isNotBlank(vo.getSearch())) {
            sb.append("and ( ")
                    .append(StrUtil.format(" n.name=~ '.*{}.*' ", vo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.title=~ '.*{}.*' ", vo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.infoProvider=~ '.*{}.*' ", vo.getSearch()))
                    .append(" or ")
                    .append(StrUtil.format(" n.infoContent=~ '.*{}.*' ", vo.getSearch()))
                    .append(") ");
        }

        Session session = sessionFactory.openSession();

        try {

            Iterator<Map<String, Object>> iterator = session.query(sb.toString() + " return count(p) as count", params).iterator();
            if (iterator.hasNext()) {
                Long total = MapUtils.getLong(iterator.next(), "count");
                params.put("size", vo.getSize());
                params.put("skip", (vo.getCurrent() - 1) * vo.getSize());
                Iterator<InfoWellRelation> relationIterator = session.query(InfoWellRelation.class, sb.toString() + " return p order by n.time desc skip $skip limit $size", params).iterator();
                return JsonResult.success(PageResult.<InfoWellRelation>builder()
                        .current(vo.getCurrent())
                        .size(vo.getSize())
                        .pages(PageUtil.totalPage(total.intValue(), vo.getSize()))
                        .total(total)
                        .records(ListUtil.toList(relationIterator))
                        .build()
                );
            }
        } finally {
            session.clear();
        }
        return JsonResult.actionFailure();
    }
}
